package drds.server.sqlparser.visitor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import drds.server.sqlparser.sqlobject.SqlObject;
import drds.server.sqlparser.sqlobject.expression.AggregateFunction;
import drds.server.sqlparser.sqlobject.expression.Between;
import drds.server.sqlparser.sqlobject.expression.Expression;
import drds.server.sqlparser.sqlobject.expression.InList;
import drds.server.sqlparser.sqlobject.expression.Not;
import drds.server.sqlparser.sqlobject.expression.binary.BinaryExpression;
import drds.server.sqlparser.sqlobject.expression.binary.BinaryOperator;
import drds.server.sqlparser.sqlobject.expression.data_type.Text;
import drds.server.sqlparser.sqlobject.expression.data_type.Value;
import drds.server.sqlparser.sqlobject.expression.data_type.number.Numeric;
import drds.server.sqlparser.sqlobject.expression.name.IName;
import drds.server.sqlparser.sqlobject.expression.name.Identifier;
import drds.server.sqlparser.sqlobject.expression.name.TableAliasName$ColumnName;
import drds.server.sqlparser.sqlobject.expression.unary.Unary;
import drds.server.sqlparser.sqlobject.statement.Statement;
import drds.server.sqlparser.sqlobject.statement.delete.DeleteStatement;
import drds.server.sqlparser.sqlobject.statement.insert.InsertStatement;
import drds.server.sqlparser.sqlobject.statement.query.GroupBy;
import drds.server.sqlparser.sqlobject.statement.query.IQuery;
import drds.server.sqlparser.sqlobject.statement.query.Limit;
import drds.server.sqlparser.sqlobject.statement.query.Query;
import drds.server.sqlparser.sqlobject.statement.query.SelectItem;
import drds.server.sqlparser.sqlobject.statement.query.SelectStatement;
import drds.server.sqlparser.sqlobject.statement.query.SubQuery;
import drds.server.sqlparser.sqlobject.statement.query.order_by.OrderBy;
import drds.server.sqlparser.sqlobject.statement.query.union_query.UnionOperator;
import drds.server.sqlparser.sqlobject.statement.query.union_query.UnionQuery;
import drds.server.sqlparser.sqlobject.statement.table.ExpressionTable;
import drds.server.sqlparser.sqlobject.statement.table.ITable;
import drds.server.sqlparser.sqlobject.statement.table.JoinTable;
import drds.server.sqlparser.sqlobject.statement.update.UpdateStatement;

/**
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class VisitorUtils {
	/**
	 * 整个切分系统最核心的算法之一。
	 */
	public static List<Expression> split(BinaryExpression x) {
		List<Expression> expressionList = new ArrayList<Expression>();
		expressionList.add(x.getRight());

		Expression left = x.getLeft();
		for (;;) {
			if (left instanceof BinaryExpression //
					&& //
					((BinaryExpression) left).getBinaryOperator() == x.getBinaryOperator()
			//
			) {
				BinaryExpression leftBinaryExpression = (BinaryExpression) left;
				expressionList.add(leftBinaryExpression.getRight());
				left = leftBinaryExpression.getLeft();
			} else {
				expressionList.add(left);
				break;
			}
		}
		return expressionList;
	}

	public final static String HAS_TRUE_LIKE = "hasTrueLike";
	private static final String EVAL_VALUE = null;

	public static boolean check(BinaryExpression x) {

		if (x.getBinaryOperator() == BinaryOperator.Or || //
				x.getBinaryOperator() == BinaryOperator.And) {//
			List<Expression> expressionList = split(x);
			for (@SuppressWarnings("unused")
			Expression expression : expressionList) {
				// item.accept(visitor);
			}
			return false;
		}

		if (x.getBinaryOperator() == BinaryOperator.Add) {
			List<Expression> expressionList = split(x);
			if (expressionList.size() >= 4) {
				int chrCount = 0;
				for (int i = 0; i < expressionList.size(); ++i) {
					Expression expression = expressionList.get(i);
					if (expression instanceof Text) {
						if (((Text) expression).getText().length() > 5) {
							chrCount = 0;
							continue;
						}
					}

					if (chrCount >= 4) {
						// addViolation( ErrorCode.EVIL_CONCAT,
						// "evil concat", x);
						break;
					}
				}
			}
		}

		return true;
	}

	public static Object getValue(BinaryExpression x) {
		if (x.getBinaryOperator() == BinaryOperator.Or) {
			List<Expression> expressionList = split(x);

			boolean allFalse = true;
			// 从后面到前面
			for (int i = expressionList.size() - 1; i >= 0; --i) {
				Expression expression = expressionList.get(i);
				//
				Object result = getValue(expression);
				Boolean booleanValue = EvalVisitorUtils.castToBoolean(result);
				if (Boolean.TRUE == booleanValue) {
					final WallConditionContext wallContext = VisitorUtils.getWallConditionContext();
					if (wallContext != null && !isFirst(expression)) {
						wallContext.setPartAlwayTrue(true);
					}
					return true;
				}

				if (Boolean.FALSE != booleanValue) {
					allFalse = false;
				}
			}

			if (allFalse) {
				return false;
			}

			return null;
		}

		if (x.getBinaryOperator() == BinaryOperator.And) {

			List<Expression> expressionList = split(x);

			Boolean allTrue = Boolean.TRUE;
			for (int i = expressionList.size() - 1; i >= 0; --i) {

				Expression expression = expressionList.get(i);
				Object result = getValue(expression);
				Boolean booleanValue = EvalVisitorUtils.castToBoolean(result);

				if (Boolean.TRUE == booleanValue) {
					final WallConditionContext wallContext = VisitorUtils.getWallConditionContext();
					if (wallContext != null && !isFirst(expression)) {
						wallContext.setPartAlwayTrue(true);
					}
				} else if (Boolean.FALSE == booleanValue) {
					final WallConditionContext wallContext = VisitorUtils.getWallConditionContext();
					if (wallContext != null && !isFirst(expression)) {
						wallContext.setPartAlwayFalse(true);
					}
					allTrue = Boolean.FALSE;
				} else {
					if (allTrue != Boolean.FALSE) {
						allTrue = null;
					}
				}

			}

			if (Boolean.TRUE == allTrue) {
				return true;
			} else if (Boolean.FALSE == allTrue) {
				return false;
			}
			return null;
		}

		boolean checkCondition = true;

		if (x.getLeft() instanceof IName) {
			if (x.getRight() instanceof IName) {
				if (x.getLeft().toString().equalsIgnoreCase(x.getRight().toString())) {
					switch (x.getBinaryOperator()) {
					case Equal:
					case Like:
						return Boolean.TRUE;
					case NotEqual:
					case GreaterThan:
					case GreaterThanOrEqual:
					case LessThan:
					case LessThanOrEqual:
					case NotLike:
						return Boolean.FALSE;
					default:
						break;
					}
				}
			} else if (!checkCondition) {
				switch (x.getBinaryOperator()) {
				case Equal:
				case NotEqual:
				case GreaterThan:
				case GreaterThanOrEqual:
				case LessThan:
				case LessThanOrEqual:
					return null;
				default:
					break;
				}
			}
		}

		if (x.getLeft() instanceof Value && x.getRight() instanceof Value) {
			Object leftValue = ((Value) x.getLeft()).getValue();
			Object rightValue = ((Value) x.getRight()).getValue();
			if (x.getBinaryOperator() == BinaryOperator.Equal) {
				boolean evalValue = EvalVisitorUtils.eq(leftValue, rightValue);
				x.putAttribute(EVAL_VALUE, evalValue);
				return evalValue;
			} else if (x.getBinaryOperator() == BinaryOperator.NotEqual) {
				boolean evalValue = EvalVisitorUtils.eq(leftValue, rightValue);
				x.putAttribute(EVAL_VALUE, !evalValue);
				return !evalValue;
			}
		}

		Object leftResult = getValue(x.getLeft());
		Object rightResult = getValue(x.getRight());

		if (x.getBinaryOperator() == BinaryOperator.Like && leftResult instanceof String && leftResult.equals(rightResult)) {
			// addViolation(ErrorCode.SAME_CONST_LIKE, "same const like", x);
		}

		if (x.getBinaryOperator() == BinaryOperator.Like || x.getBinaryOperator() == BinaryOperator.NotLike) {
			if (rightResult instanceof Number || leftResult instanceof Number) {
				// context.incrementLikeNumberWarnings();
			}
		}

		return eval(x, Collections.emptyList());
	}

	public static Expression getFirst(Expression x) {
		if (x instanceof BinaryExpression) {
			BinaryExpression binaryExpression = (BinaryExpression) x;
			if (binaryExpression.getBinaryOperator() == BinaryOperator.And //
					|| //
					binaryExpression.getBinaryOperator() == BinaryOperator.Or) {//
				return getFirst(((BinaryExpression) x).getLeft());
			}
		}

		return x;
	}

	public static List<Expression> getParts(Expression x) {
		List<Expression> expressionList = new ArrayList<Expression>();
		expressionList.add(x);

		while (true) {
			List<Expression> tmp = partExpressionList(expressionList);

			if (tmp.size() == expressionList.size()) {
				break;
			}
			expressionList = tmp;
		}

		return expressionList;
	}

	public static List<Expression> partExpressionList(List<Expression> exprs) {
		List<Expression> partList = new ArrayList<Expression>();

		for (Expression x : exprs) {
			if (x instanceof BinaryExpression) {
				BinaryExpression binary = (BinaryExpression) x;
				if (binary.getBinaryOperator() == BinaryOperator.And || binary.getBinaryOperator() == BinaryOperator.Or) {
					partList.add(((BinaryExpression) x).getLeft());
					partList.add(((BinaryExpression) x).getRight());

					continue;
				}
			}
			partList.add(x);
		}
		return partList;
	}

	public static boolean isFirst(SqlObject x) {
		if (x == null) {
			return true;
		}

		for (;;) {
			SqlObject parent = x.getParent();
			if (!(parent instanceof Expression)) {
				return true;
			}

			if (parent instanceof BinaryExpression) {
				BinaryExpression binaryExpr = (BinaryExpression) parent;
				if (x == binaryExpr.getRight()) {
					return false;
				}
			}
			x = parent;
		}
	}

	private static boolean hasWhere(IQuery query) {

		if (query instanceof Query) {
			return ((Query) query).getWhere() != null;
		} else if (query instanceof UnionQuery) {
			UnionQuery unionQuery = (UnionQuery) query;
			return hasWhere(unionQuery.getLeft()) //
					|| //
					hasWhere(unionQuery.getRight());//
		}
		return false;
	}

	public static boolean checkSqlExpr(Expression x) { // check groupby,
														// orderby,
		// limit
		if (x == null) {
			return false;
		}

		SqlObject sqlObject = x;
		for (;;) {
			SqlObject parent = sqlObject.getParent();

			if (parent == null) {
				return false;
			}

			if (parent instanceof GroupBy) {
				return true;
			} else if (parent instanceof OrderBy) {
				return true;
			} else if (parent instanceof Limit) {
				return true;
			}

			sqlObject = parent;
		}
	}

	public static boolean isWhereOrHaving(SqlObject x) {
		if (x == null) {
			return false;
		}

		for (;;) {
			SqlObject parent = x.getParent();

			if (parent == null) {
				return false;
			}

			if (parent instanceof JoinTable) {
				JoinTable joinTable = (JoinTable) parent;
				if (joinTable.getOn() == x) {
					return true;
				}

			}

			if (parent instanceof UnionQuery) {
				UnionQuery unionQuery = (UnionQuery) parent;
				if (unionQuery.getRight() == x && hasWhere(unionQuery.getLeft())) {
					return true;
				}
			}

			if (parent instanceof Query) {
				Query query = (Query) parent;
				if (query.getWhere() == x) {
					return true;
				}
			}

			if (parent instanceof DeleteStatement) {
				DeleteStatement deleteStatement = (DeleteStatement) parent;
				if (deleteStatement.getWhere() == x) {
					return true;
				} else {
					return false;
				}
			}

			if (parent instanceof UpdateStatement) {
				UpdateStatement updateStatement = (UpdateStatement) parent;
				if (updateStatement.getWhere() == x) {
					return true;
				} else {
					return false;
				}
			}

			if (parent instanceof GroupBy) {
				GroupBy groupBy = (GroupBy) parent;
				if (x == groupBy.getFilter()) {
					return true;
				} else {
					return false;
				}
			}

			x = parent;
		}
	}

	public static class WallTopStatementContext {

		private boolean fromSysTable = false;
		private boolean fromSysSchema = false;

		private boolean fromPermitTable = false;

		public boolean fromSysTable() {
			return fromSysTable;
		}

		public void setFromSysTable(boolean fromSysTable) {
			this.fromSysTable = fromSysTable;
		}

		public boolean fromSysSchema() {
			return fromSysSchema;
		}

		public void setFromSysSchema(boolean fromSysSchema) {
			this.fromSysSchema = fromSysSchema;
		}

		public boolean fromPermitTable() {
			return fromPermitTable;
		}

		public void setFromPermitTable(boolean fromPermitTable) {
			this.fromPermitTable = fromPermitTable;
		}
	}

	public static class WallConditionContext {

		private boolean partAlwayTrue = false;
		private boolean partAlwayFalse = false;
		private boolean constArithmetic = false;
		private boolean xor = false;
		private boolean bitwise = false;

		public boolean hasPartAlwayTrue() {
			return partAlwayTrue;
		}

		public void setPartAlwayTrue(boolean partAllowTrue) {
			this.partAlwayTrue = partAllowTrue;
		}

		public boolean hasPartAlwayFalse() {
			return partAlwayFalse;
		}

		public void setPartAlwayFalse(boolean partAlwayFalse) {
			this.partAlwayFalse = partAlwayFalse;
		}

		public boolean hasConstArithmetic() {
			return constArithmetic;
		}

		public void setConstArithmetic(boolean constArithmetic) {
			this.constArithmetic = constArithmetic;
		}

		public boolean hasXor() {
			return xor;
		}

		public void setXor(boolean xor) {
			this.xor = xor;
		}

		public boolean hasBitwise() {
			return bitwise;
		}

		public void setBitwise(boolean bitwise) {
			this.bitwise = bitwise;
		}

	}

	private static ThreadLocal<WallConditionContext> wallConditionContextLocal = new ThreadLocal<WallConditionContext>();
	private static ThreadLocal<WallTopStatementContext> wallTopStatementContextLocal = new ThreadLocal<WallTopStatementContext>();

	public static WallConditionContext getWallConditionContext() {
		return wallConditionContextLocal.get();
	}

	public static WallTopStatementContext getWallTopStatementContext() {
		return wallTopStatementContextLocal.get();
	}

	public static void clearWallTopStatementContext() {
		wallTopStatementContextLocal.set(null);
	}

	public static void initWallTopStatementContext() {
		wallTopStatementContextLocal.set(new WallTopStatementContext());
	}

	public static Object getConditionValue(Expression x, boolean alwayTrueCheck) {
		final WallConditionContext old = wallConditionContextLocal.get();
		try {
			wallConditionContextLocal.set(new WallConditionContext());
			final Object value = getValue(x);

			final WallConditionContext current = wallConditionContextLocal.get();
			if (current.hasPartAlwayTrue() || Boolean.TRUE == value) {
				if (!isFirst(x)) {
				}
			}

			return value;
		} finally {
			wallConditionContextLocal.set(old);
		}
	}

	public static Object getValueFromAttributes(SqlObject sqlObject) {
		if (sqlObject == null) {
			return null;
		}
		return sqlObject.getAttribute(EVAL_VALUE);
	}

	/**
	 * 获取表达式的值
	 */
	public static Object getValue(Expression x) {
		if (x != null && x.getAttributeMap().containsKey(EVAL_VALUE)) {
			return getValueFromAttributes(x);
		}

		if (x instanceof BinaryExpression) {
			return getValue((BinaryExpression) x);
		}

		if (x instanceof Numeric) {
			return ((Numeric) x).getNumber();
		}

		if (x instanceof Text) {
			return ((Text) x).getText();
		}

		if (x instanceof Not) {
			Object result = getValue(((Not) x).getExpression());
			if (result instanceof Boolean) {
				return !((Boolean) result).booleanValue();
			}
		}

		if (x instanceof Between //
				|| x instanceof InList //
				|| x instanceof Unary //
		) {
			return eval(x, Collections.emptyList());
		}

		return null;
	}

	public static Object eval(SqlObject sqlObject, List<Object> parameters) {
		EvalVisitor visitor = EvalVisitorUtils.createEvalVisitor();
		sqlObject.accept(visitor);

		if (sqlObject instanceof Numeric) {
			return ((Numeric) sqlObject).getNumber();
		}
		return getValueFromAttributes(sqlObject);
	}

	public static boolean isSimpleCountTable(SubQuery subQuery) {
		IQuery iQuery = subQuery.getIQuery();

		if (iQuery instanceof Query) {
			Query query = (Query) iQuery;

			boolean allawTrueWhere = false;

			if (query.getWhere() == null) {
				allawTrueWhere = true;
			} else {
				Object whereValue = getValue(query.getWhere());
				if (whereValue == Boolean.TRUE) {
					allawTrueWhere = true;
				} else if (whereValue == Boolean.FALSE) {
					return false;
				}
			}
			boolean simpleCount = false;
			if (query.getSelectItemList().size() == 1) {
				Expression expression = query.getSelectItemList().get(0).getExpression();
				if (expression instanceof AggregateFunction) {
					if (((AggregateFunction) expression).getMethodName().equalsIgnoreCase("count")) {
						simpleCount = true;
					}
				}
			}

			if (allawTrueWhere && simpleCount) {
				return true;
			}
		}

		return false;
	}

	public static boolean isSimpleTable(SubQuery subQuery) {
		IQuery iQuery = subQuery.getIQuery();

		if (iQuery instanceof Query) {
			Query query = (Query) iQuery;

			boolean allawTrueWhere = false;

			if (query.getWhere() == null) {
				allawTrueWhere = true;
			} else {
				Object whereValue = getValue(query.getWhere());
				if (whereValue == Boolean.TRUE) {
					allawTrueWhere = true;
				} else if (whereValue == Boolean.FALSE) {
					return false;
				}
			}
			boolean simpleCase = false;
			if (query.getSelectItemList().size() == 1) {
			}

			if (allawTrueWhere && simpleCase) {
				return true;
			}
		}

		return false;
	}

	public static Query getQuery(SqlObject x) {
		if (x == null) {
			return null;
		}

		if (x instanceof Query) {
			return (Query) x;
		}

		SqlObject parent = x.getParent();

		if (parent instanceof Expression) {
			return getQuery(parent);
		}

		if (parent instanceof SelectItem) {
			return getQuery(parent);
		}

		if (parent instanceof Query) {
			return (Query) parent;
		}

		return null;
	}

	public static boolean isTopNoneFromSelect(SqlObject x) {
		for (;;) {
			if ((x.getParent() instanceof Expression)) {
				x = x.getParent();
			} else {
				break;
			}
		}

		if (!(x.getParent() instanceof SelectItem)) {
			return false;
		}
		SelectItem selectItem = (SelectItem) x.getParent();

		if (!(selectItem.getParent() instanceof Query)) {
			return false;
		}

		Query query = (Query) selectItem.getParent();
		if (!queryBlockFromIsNull(query)) {
			return false;
		}

		if (!(query.getParent() instanceof SubQuery)) {
			return false;
		}

		SubQuery subQuery = (SubQuery) query.getParent();

		if (!(subQuery.getParent() instanceof SelectStatement)) {
			return false;
		}

		SelectStatement selectStatement = (SelectStatement) subQuery.getParent();

		return selectStatement.getParent() == null;
	}

	private static boolean checkSchema(Expression x) {
		final WallTopStatementContext topStatementContext = wallTopStatementContextLocal.get();
		if (topStatementContext != null && (topStatementContext.fromSysSchema || topStatementContext.fromSysTable)) {
			return true;
		}

		if (x instanceof IName) {
			String owner = ((IName) x).getName();
			if (isInTableSource(x)) {

				if (!isFirstSelectTable(x) && !isFirstInSubQuery(x)) {
					SqlObject parent = x.getParent();
					while (parent != null && !(parent instanceof Statement)) {
						parent = parent.getParent();
					}

					boolean sameToTopSelectSchema = false;
					if (parent instanceof SelectStatement) {
						SelectStatement selectStmt = (SelectStatement) parent;
						IQuery query = selectStmt.getSubQuery().getIQuery();
						if (query instanceof Query) {
							Query queryBlock = (Query) query;
							ITable from = queryBlock.getFrom();

							while (from instanceof JoinTable) {
								from = ((JoinTable) from).getLeft();
							}
							if (from instanceof ExpressionTable) {
								Expression expression = ((ExpressionTable) from).getExpression();
								if (expression instanceof TableAliasName$ColumnName) {
									Expression tableNameOrTableAliasName = ((TableAliasName$ColumnName) expression).getTableAliasName();
									if (tableNameOrTableAliasName instanceof Identifier) {
										String schema = ((Identifier) tableNameOrTableAliasName).getName();
										if (schema.equalsIgnoreCase(owner)) {
											sameToTopSelectSchema = true;
										}
									}
								}
							}
						}
					}

					if (!sameToTopSelectSchema) {
						// addViolation(ErrorCode.SCHEMA_DENY, "deny schema : "
						// + owner, x);
					}
				} else {
					if (topStatementContext != null) {
						topStatementContext.setFromSysSchema(Boolean.TRUE);
						// clearViolation(visitor);
					}
				}
				return true;
			}

		}

		// if (ownerExpr instanceof SQLPropertyExpr) {
		if (x instanceof TableAliasName$ColumnName) {
			return checkSchema(((TableAliasName$ColumnName) x).getTableAliasName());
		}

		return true;
	}

	private static boolean isInTableSource(SqlObject x) {

		for (;;) {
			if (x instanceof Expression) {
				x = x.getParent();
			} else {
				break;
			}
		}

		if (x instanceof ExpressionTable) {
			return true;
		}
		return false;
	}

	private static boolean isFirstInSubQuery(SqlObject x) {
		for (;;) {
			if (x instanceof Expression) {
				x = x.getParent();
			} else {
				break;
			}
		}

		if (!(x instanceof ExpressionTable)) {
			return false;
		}

		SubQuery subQuery = null;
		SqlObject parent = x.getParent();
		while (parent != null) {

			if (parent instanceof SubQuery) {
				subQuery = (SubQuery) parent;
				break;
			}

			x = parent;
			parent = x.getParent();
		}

		if (subQuery == null) {
			return false;
		}

		return true;
	}

	private static boolean isFirstSelectTable(SqlObject x) {

		for (;;) {
			if (x instanceof Expression) {
				x = x.getParent();
			} else {
				break;
			}
		}

		if (!(x instanceof ExpressionTable)) {
			return false;
		}

		Query query = null;
		SqlObject parent = x.getParent();
		while (parent != null) {

			// if (parent instanceof SQLJoinTableSource) {
			// SQLJoinTableSource join = (SQLJoinTableSource) parent;
			// if (join.getRight() == x && hasTableSource(join.getLeft())) {
			// return false;
			// }
			// }

			if (parent instanceof Query) {
				query = (Query) parent;
				break;
			}

			x = parent;
			parent = x.getParent();
		}

		if (query == null) {
			return false;
		}

		boolean isWhereQueryExpr = false;
		boolean isSelectItem = false;
		do {
			x = parent;
			parent = parent.getParent();
			if (parent instanceof UnionQuery) {
				UnionQuery unionQuery = (UnionQuery) parent;
				if (unionQuery.getRight() == x && hasTable(unionQuery.getLeft())) {
					return false;
				}
			} else if (parent instanceof SelectItem) {
				isSelectItem = true;
			} else if ((isWhereQueryExpr || isSelectItem) && parent instanceof Query) {
				if (hasTable((Query) parent)) {
					return false;
				}
			}

		} while (parent != null);

		return true;
	}

	private static boolean hasTable(IQuery x) {

		if (x instanceof UnionQuery) {
			UnionQuery union = (UnionQuery) x;
			return hasTable(union.getLeft()) || hasTable(union.getRight());
		} else if (x instanceof Query) {
			return hasTable(((Query) x).getFrom());
		}

		return false;
	}

	private static boolean hasTable(ITable x) {
		if (x == null) {
			return false;
		}

		if (x instanceof ExpressionTable) {
			Expression expression = ((ExpressionTable) x).getExpression();
			if (expression instanceof IName) {
				String name = expression.toString();
				if (name.equalsIgnoreCase("DUAL")) {
					return false;
				}
			}
			return true;
		} else if (x instanceof JoinTable) {
			JoinTable joinTable = (JoinTable) x;
			return hasTable(joinTable.getLeft()) || hasTable(joinTable.getRight());
		}

		return false;
	}

	public static boolean check(ExpressionTable x) {
		final WallTopStatementContext topStatementContext = wallTopStatementContextLocal.get();

		Expression expression = x.getExpression();
		if (expression instanceof TableAliasName$ColumnName) {
			boolean checkResult = checkSchema(((TableAliasName$ColumnName) expression).getTableAliasName());
			if (!checkResult) {
				return false;
			}
		}

		if (expression instanceof IName) {
			SqlObject parent = x.getParent();

			while (parent instanceof ITable) {
				parent = parent.getParent();
			}

			if (topStatementContext != null && (topStatementContext.fromSysSchema || topStatementContext.fromSysTable)) {
				return true;
			}

			if (isFirstSelectTable(x)) {
				if (topStatementContext != null) {
					topStatementContext.setFromPermitTable(Boolean.TRUE);
				}
				return false;
			}
		}

		return true;
	}

	public static void checkUnion(UnionQuery x) {

		if (!VisitorUtils.queryBlockFromIsNull(x.getLeft()) && VisitorUtils.queryBlockFromIsNull(x.getRight())) {
			boolean isTopUpdateStatement = false;
			boolean isTopInsertStatement = false;
			SqlObject selectParent = x.getParent();
			while (selectParent instanceof IQuery //
					|| selectParent instanceof JoinTable //
					|| selectParent instanceof SubQuery) {
				selectParent = selectParent.getParent();
			}

			if (selectParent instanceof UpdateStatement) {
				isTopUpdateStatement = true;
			}

			if (selectParent instanceof InsertStatement) {
				isTopInsertStatement = true;
			}

			if (isTopUpdateStatement || isTopInsertStatement) {
				return;
			}

			if (x.getLeft() instanceof Query) {
				Query left = (Query) x.getLeft();
				ITable tableSource = left.getFrom();
				if (left.getWhere() == null && tableSource != null && tableSource instanceof ExpressionTable) {
					return;
				}
			}

			if (((x.getUnionOperator() == UnionOperator.UNION || x.getUnionOperator() == UnionOperator.UNION_ALL || x.getUnionOperator() == UnionOperator.DISTINCT))) {
				// addViolation(ErrorCode.UNION, x.getOperator().toString() +
				// " query not contains 'from clause'", x);
			}
		}
	}

	public static boolean queryBlockFromIsNull(IQuery query) {
		return queryBlockFromIsNull(query, true);
	}

	public static boolean queryBlockFromIsNull(IQuery iQuery, boolean checkSelectConst) {
		if (iQuery instanceof Query) {
			Query query = (Query) iQuery;
			ITable from = query.getFrom();

			if (query.getSelectItemList().size() < 1) {
				return false;
			}

			if (from == null) {
				boolean itemIsConst = true;
				boolean itemHasAlias = false;
				for (SelectItem selectItem : query.getSelectItemList()) {
					if (selectItem.getExpression() instanceof Identifier || selectItem.getExpression() instanceof TableAliasName$ColumnName) {
						itemIsConst = false;
						break;
					}
					if (selectItem.getAlias() != null) {
						itemHasAlias = true;
						break;
					}
				}
				if (itemIsConst && !itemHasAlias) {
					return true;
				} else {
					return false;
				}
			}
			if (checkSelectConst) {
				Expression where = query.getWhere();
				if (where != null) {
					Object whereValue = getValue(where);
					if (Boolean.TRUE == whereValue) {
						boolean allIsConst = true;
						for (SelectItem item : query.getSelectItemList()) {
							if (getValue(item.getExpression()) == null) {
								allIsConst = false;
								break;
							}
						}
						if (allIsConst) {
							return true;
						}
					}
				}
			}
		}

		return false;
	}

}
